// 高级音高检测算法 - 结合自相关法和谐波乘积谱(HPS)
export class AdvancedPitchDetector {
  constructor() {
    this.audioContext = null;
    this.audioBuffer = null;
    this.sampleRate = 44100;
    this.minFreq = 80;    // 最小检测频率 (Hz)
    this.maxFreq = 2000;  // 最大检测频率 (Hz)
  }

  // 初始化音频上下文
  async initAudioContext() {
    if (!this.audioContext) {
      this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
    }
  }

  // 加载音频文件
  async loadAudioFile(file) {
    await this.initAudioContext();
    
    const arrayBuffer = await file.arrayBuffer();
    this.audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
    this.sampleRate = this.audioBuffer.sampleRate;
    
    return {
      duration: this.audioBuffer.duration,
      sampleRate: this.sampleRate,
      numberOfChannels: this.audioBuffer.numberOfChannels
    };
  }

  // 从音频缓冲区获取指定时间段的数据
  getAudioSegment(startTime, endTime) {
    if (!this.audioBuffer) {
      throw new Error('音频文件未加载');
    }

    const startSample = Math.floor(startTime * this.sampleRate);
    const endSample = Math.floor(endTime * this.sampleRate);
    
    // 获取第一个声道的数据
    const channelData = this.audioBuffer.getChannelData(0);
    const segment = channelData.slice(startSample, endSample);

    return segment;
  }

  // 主要的音高检测函数 - 结合多种算法
  detectPitch(audioData, windowSize = 4096) {
    if (audioData.length < windowSize) {
      return { frequency: 0, confidence: 0, method: 'insufficient_data' };
    }

    // 预处理：应用窗函数并归一化
    const processedData = this.preprocessAudio(audioData, windowSize);
    
    // 方法1: 自相关法检测基频
    const autocorrelationResult = this.autocorrelationPitch(processedData);
    
    // 方法2: 谐波乘积谱法 (HPS)
    const hpsResult = this.harmonicProductSpectrum(processedData);
    
    // 方法3: 改进的FFT峰值检测（带谐波分析）
    const fftResult = this.improvedFFTPitch(processedData);
    
    // 综合多种方法的结果，选择最可信的
    const finalResult = this.combineResults([autocorrelationResult, hpsResult, fftResult]);
    
    return finalResult;
  }

  // 音频预处理
  preprocessAudio(data, windowSize) {
    const windowed = new Array(windowSize).fill(0);
    const actualLength = Math.min(data.length, windowSize);
    
    // 应用汉宁窗
    for (let i = 0; i < actualLength; i++) {
      const windowValue = 0.5 * (1 - Math.cos(2 * Math.PI * i / (actualLength - 1)));
      windowed[i] = data[i] * windowValue;
    }
    
    // 归一化
    const maxValue = Math.max(...windowed.map(x => Math.abs(x)));
    if (maxValue > 0) {
      for (let i = 0; i < windowed.length; i++) {
        windowed[i] /= maxValue;
      }
    }
    
    return windowed;
  }

  // 自相关法检测音高
  autocorrelationPitch(data) {
    const length = data.length;
    const autocorrelation = new Array(length).fill(0);
    
    // 计算自相关函数
    for (let lag = 0; lag < length; lag++) {
      let sum = 0;
      for (let i = 0; i < length - lag; i++) {
        sum += data[i] * data[i + lag];
      }
      autocorrelation[lag] = sum / (length - lag);
    }
    
    // 找到第一个负值后的最大峰值
    let firstNegative = 1;
    while (firstNegative < autocorrelation.length && autocorrelation[firstNegative] >= 0) {
      firstNegative++;
    }
    
    // 在合理范围内寻找最大峰值
    const minLag = Math.max(firstNegative, Math.floor(this.sampleRate / this.maxFreq));
    const maxLag = Math.min(autocorrelation.length - 1, Math.floor(this.sampleRate / this.minFreq));
    
    let maxValue = -Infinity;
    let bestLag = 0;
    
    for (let lag = minLag; lag <= maxLag; lag++) {
      if (autocorrelation[lag] > maxValue) {
        maxValue = autocorrelation[lag];
        bestLag = lag;
      }
    }
    
    // 使用抛物线插值提高精度
    const interpolatedLag = this.parabolicInterpolation(autocorrelation, bestLag);
    const frequency = interpolatedLag > 0 ? this.sampleRate / interpolatedLag : 0;
    const confidence = maxValue / autocorrelation[0]; // 归一化置信度
    
    return {
      frequency: frequency,
      confidence: confidence,
      method: 'autocorrelation'
    };
  }

  // 谐波乘积谱法 (HPS)
  harmonicProductSpectrum(data) {
    const fftSize = this.nextPowerOf2(data.length);
    const paddedData = [...data];
    while (paddedData.length < fftSize) paddedData.push(0);
    
    // 计算FFT
    const fftResult = this.fft(paddedData.map(x => ({ real: x, imag: 0 })));
    const magnitudes = fftResult.slice(0, fftSize / 2).map(c => 
      Math.sqrt(c.real * c.real + c.imag * c.imag)
    );
    
    // 计算HPS：将频谱与其下采样版本相乘
    const hpsSpectrum = [...magnitudes];
    const harmonics = 5; // 考虑前5个谐波
    
    for (let h = 2; h <= harmonics; h++) {
      for (let i = 0; i < hpsSpectrum.length; i++) {
        const harmonicIndex = Math.floor(i / h);
        if (harmonicIndex < magnitudes.length) {
          hpsSpectrum[i] *= magnitudes[harmonicIndex];
        }
      }
    }
    
    // 在频率范围内找到最大峰值
    const freqResolution = this.sampleRate / fftSize;
    const minBin = Math.floor(this.minFreq / freqResolution);
    const maxBin = Math.floor(this.maxFreq / freqResolution);
    
    let maxValue = 0;
    let bestBin = 0;
    
    for (let i = minBin; i <= Math.min(maxBin, hpsSpectrum.length - 1); i++) {
      if (hpsSpectrum[i] > maxValue) {
        maxValue = hpsSpectrum[i];
        bestBin = i;
      }
    }
    
    // 使用抛物线插值
    const interpolatedBin = this.parabolicInterpolation(hpsSpectrum, bestBin);
    const frequency = interpolatedBin * freqResolution;
    
    // 计算置信度（峰值与周围平均值的比率）
    const confidence = this.calculatePeakConfidence(hpsSpectrum, bestBin);
    
    return {
      frequency: frequency,
      confidence: confidence,
      method: 'hps'
    };
  }

  // 改进的FFT音高检测（带谐波分析）
  improvedFFTPitch(data) {
    const fftSize = this.nextPowerOf2(data.length);
    const paddedData = [...data];
    while (paddedData.length < fftSize) paddedData.push(0);
    
    // 计算FFT
    const fftResult = this.fft(paddedData.map(x => ({ real: x, imag: 0 })));
    const magnitudes = fftResult.slice(0, fftSize / 2).map(c => 
      Math.sqrt(c.real * c.real + c.imag * c.imag)
    );
    
    const freqResolution = this.sampleRate / fftSize;
    const minBin = Math.floor(this.minFreq / freqResolution);
    const maxBin = Math.floor(this.maxFreq / freqResolution);
    
    // 找到所有峰值
    const peaks = this.findPeaks(magnitudes, minBin, maxBin);
    
    // 根据谐波关系评估每个峰值作为基频的可能性
    let bestCandidate = null;
    let bestScore = 0;
    
    for (const peak of peaks) {
      const frequency = peak.bin * freqResolution;
      const score = this.evaluateHarmonicStrength(magnitudes, peak.bin, freqResolution);
      
      if (score > bestScore) {
        bestScore = score;
        bestCandidate = peak;
      }
    }
    
    if (!bestCandidate) {
      return { frequency: 0, confidence: 0, method: 'fft_improved' };
    }
    
    // 使用抛物线插值
    const interpolatedBin = this.parabolicInterpolation(magnitudes, bestCandidate.bin);
    const frequency = interpolatedBin * freqResolution;
    
    return {
      frequency: frequency,
      confidence: bestScore,
      method: 'fft_improved'
    };
  }

  // 评估谐波强度
  evaluateHarmonicStrength(magnitudes, fundamentalBin, freqResolution) {
    let harmonicStrength = 0;
    const harmonicsToCheck = 5;
    
    for (let h = 1; h <= harmonicsToCheck; h++) {
      const harmonicBin = Math.round(fundamentalBin * h);
      if (harmonicBin < magnitudes.length) {
        harmonicStrength += magnitudes[harmonicBin] / h; // 权重递减
      }
    }
    
    return harmonicStrength;
  }

  // 找到频谱中的峰值
  findPeaks(magnitudes, minBin, maxBin) {
    const peaks = [];
    
    for (let i = minBin + 1; i < Math.min(maxBin, magnitudes.length - 1); i++) {
      if (magnitudes[i] > magnitudes[i - 1] && magnitudes[i] > magnitudes[i + 1]) {
        peaks.push({
          bin: i,
          magnitude: magnitudes[i]
        });
      }
    }
    
    // 按幅度排序
    peaks.sort((a, b) => b.magnitude - a.magnitude);
    
    return peaks.slice(0, 10); // 返回前10个峰值
  }

  // 抛物线插值提高频率精度
  parabolicInterpolation(array, peakIndex) {
    if (peakIndex <= 0 || peakIndex >= array.length - 1) {
      return peakIndex;
    }
    
    const y1 = array[peakIndex - 1];
    const y2 = array[peakIndex];
    const y3 = array[peakIndex + 1];
    
    const a = (y1 - 2 * y2 + y3) / 2;
    const b = (y3 - y1) / 2;
    
    if (a === 0) return peakIndex;
    
    const xOffset = -b / (2 * a);
    return peakIndex + xOffset;
  }

  // 计算峰值置信度
  calculatePeakConfidence(spectrum, peakIndex) {
    if (peakIndex <= 0 || peakIndex >= spectrum.length - 1) {
      return 0;
    }
    
    const peakValue = spectrum[peakIndex];
    
    // 计算周围区域的平均值
    const windowSize = 5;
    let sum = 0;
    let count = 0;
    
    for (let i = Math.max(0, peakIndex - windowSize); 
         i <= Math.min(spectrum.length - 1, peakIndex + windowSize); i++) {
      if (i !== peakIndex) {
        sum += spectrum[i];
        count++;
      }
    }
    
    const avgAround = count > 0 ? sum / count : 0;
    return avgAround > 0 ? peakValue / avgAround : 0;
  }

  // 综合多种方法的结果
  combineResults(results) {
    // 过滤掉无效结果
    const validResults = results.filter(r => r.frequency > this.minFreq && r.frequency < this.maxFreq);
    
    if (validResults.length === 0) {
      return { frequency: 0, confidence: 0, method: 'no_valid_result' };
    }
    
    // 如果只有一个有效结果
    if (validResults.length === 1) {
      return validResults[0];
    }
    
    // 选择置信度最高的结果
    const bestResult = validResults.reduce((best, current) => 
      current.confidence > best.confidence ? current : best
    );
    
    // 检查其他方法是否有相近的频率结果
    const tolerance = 0.05; // 5% 容差
    const confirmingResults = validResults.filter(r => 
      Math.abs(r.frequency - bestResult.frequency) / bestResult.frequency < tolerance
    );
    
    // 如果有多个方法确认，提高置信度
    if (confirmingResults.length > 1) {
      return {
        ...bestResult,
        confidence: Math.min(1.0, bestResult.confidence * 1.2),
        method: `combined_${bestResult.method}`
      };
    }
    
    return bestResult;
  }

  // 获取下一个2的幂次
  nextPowerOf2(n) {
    let power = 1;
    while (power < n) power *= 2;
    return power;
  }

  // FFT实现（Cooley-Tukey算法）
  fft(data) {
    const N = data.length;
    if (N <= 1) return data;

    // 确保长度是2的幂
    let size = 1;
    while (size < N) size *= 2;
    
    const paddedData = [...data];
    while (paddedData.length < size) paddedData.push({ real: 0, imag: 0 });

    return this.fftRecursive(paddedData);
  }

  fftRecursive(data) {
    const N = data.length;
    if (N <= 1) return data;

    const even = [];
    const odd = [];
    for (let i = 0; i < N; i += 2) {
      even.push(data[i]);
      if (i + 1 < N) odd.push(data[i + 1]);
    }

    const evenFft = this.fftRecursive(even);
    const oddFft = this.fftRecursive(odd);

    const result = new Array(N);
    for (let k = 0; k < N / 2; k++) {
      const angle = -2 * Math.PI * k / N;
      const twiddle = {
        real: Math.cos(angle),
        imag: Math.sin(angle)
      };

      const oddK = oddFft[k] || { real: 0, imag: 0 };
      const t = {
        real: twiddle.real * oddK.real - twiddle.imag * oddK.imag,
        imag: twiddle.real * oddK.imag + twiddle.imag * oddK.real
      };

      const evenK = evenFft[k] || { real: 0, imag: 0 };
      result[k] = {
        real: evenK.real + t.real,
        imag: evenK.imag + t.imag
      };
      result[k + N / 2] = {
        real: evenK.real - t.real,
        imag: evenK.imag - t.imag
      };
    }

    return result;
  }

  // 分析指定时间段的音高
  async analyzePitchInTimeRange(startTime, endTime, intervalMs = 20) {
    const audioSegment = this.getAudioSegment(startTime, endTime);
    const intervalSamples = Math.floor((intervalMs / 1000) * this.sampleRate);
    const windowSize = 4096; // 使用更大的窗口
    const results = [];

    let currentTime = startTime;
    let sampleIndex = 0;

    while (currentTime < endTime && sampleIndex < audioSegment.length) {
      const segmentEnd = Math.min(sampleIndex + windowSize, audioSegment.length);
      const segment = audioSegment.slice(sampleIndex, segmentEnd);

      if (segment.length >= windowSize / 2) { // 确保有足够的数据
        const pitchResult = this.detectPitch(segment, windowSize);
        const noteName = this.frequencyToNoteName(pitchResult.frequency);
        
        results.push({
          time: currentTime,
          frequency: pitchResult.frequency,
          confidence: pitchResult.confidence,
          method: pitchResult.method,
          noteName: noteName
        });
      }

      currentTime += intervalMs / 1000;
      sampleIndex += intervalSamples;
    }

    return results;
  }

  // 频率转换为音名（改进版本）
  frequencyToNoteName(frequency) {
    if (frequency <= 0 || !isFinite(frequency)) {
      return { note: '无', octave: '', fullName: '无音高' };
    }

    const noteNames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
    const A4 = 440;
    const A4_INDEX = 9;
    const A4_OCTAVE = 4;

    // 计算相对于A4的半音数（使用更精确的方法）
    const semitonesFromA4 = 12 * Math.log2(frequency / A4);
    const roundedSemitones = Math.round(semitonesFromA4);
    
    // 计算音偏差（cents）
    const cents = Math.round((semitonesFromA4 - roundedSemitones) * 100);
    
    // 计算音名索引和八度
    const noteIndex = ((A4_INDEX + roundedSemitones) % 12 + 12) % 12;
    const octave = A4_OCTAVE + Math.floor((A4_INDEX + roundedSemitones) / 12);
    
    const note = noteNames[noteIndex];
    const fullName = `${note}${octave}`;

    return {
      note: note,
      octave: octave.toString(),
      fullName: fullName,
      cents: cents // 添加音分信息
    };
  }
}



